---
title: "Service Map"
type: concept
created: 2026-04-18
updated: 2026-04-18
sources: ["raw/articles/00-overview.md", "raw/articles/06-reading-telemetry.md", "raw/articles/10-build-checklist.md"]
tags: [architecture, services, ports, stack, disk]
---

# Service Map

Complete reference for every service in the Pickatale platform. Ground truth from code audit 2026-04-18.

## User-Facing Services

### Reader App
- **Domain:** app.readingtester.com | **Server:** :3125 | **Client dev:** :5193
- **Stack:** React 19 + Vite + TypeScript (client) · Express + tRPC + Drizzle ORM (server)
- **DB:** `reader_app` on shared MySQL
- **Disk:** `/home/ubuntu/reader/`
- **Status:** ✅ Live
- **Key features:** 9,089 books from CDN, Finger-Follow TTS, Karaoke TTS, Speedread RSVP, Miles + Tokens, PWA offline-first, ElevenLabs TTS

### Teacher Portal
- **Domain:** teacher.readingtester.com | **Server:** :3116 | **Client dev:** :5190
- **Stack:** Next.js + Express
- **DB:** `teacher_portal` on shared MySQL
- **Disk:** `/home/ubuntu/teacher-portal/`
- **Status:** ✅ Infrastructure built. 1 teacher, 0 real students.
- **Known bug:** Class name display mismatch (input "Year3Blue" shows "Year 3 Red" for class ID 4 — root cause unknown)

### Parent Portal
- **Domain:** parents.readingtester.com | **Server:** :3118 | **Client dev:** :5192
- **Disk:** `/home/ubuntu/parent-portal/`
- **Status:** ✅ Infrastructure built. No real digests yet.

### Account Center (SSO Hub)
- **Domain:** account.readingtester.com | **Server:** :3126
- **Stack:** Node.js + Express
- **DB:** `account_center` on shared MySQL
- **Status:** ✅ Auth built (login, register, uc_session, /api/auth/session). learner_profiles schema NOT YET ADDED.

### Curriculum Mapper
- **Domain:** cm.readingtester.com | **Server:** :3100 | **Client dev:** :5176
- **Stack:** tRPC (REST API does not exist yet — Phase 2 must-build)
- **Disk:** `/home/ubuntu/cm/`
- **DB:** `cm` on shared MySQL
- **Status:** ✅ Live — 3,777 objectives (Turkey 3,692 · England 85)
- **Critical gap:** Orchestrator cannot query CM without REST API — `GET /api/v1/objectives` must be built

### Adaptive Content Engine
- **Domain:** adapt.readingtester.com | **Server:** :3119
- **Disk:** `/home/ubuntu/adaptive-content/`
- **DB:** `adaptive_content` on shared MySQL
- **Status:** ✅ Live. GPT-4o FK levelling works. Translation unavailable (no DeepL/Google keys).
- **ZPD offset:** child FK level + 0.5 (slightly above current level)
- **Known issue:** FK scores sometimes unchanged. GPT prompt tightening required.

## Internal Services

### Telemetry Service
- **Domain:** (internal) | **Server:** :3110 | **Client dev:** :5185
- **Disk:** `/home/ubuntu/telemetry/`
- **DB:** `telemetry` on shared MySQL
- **Status:** ✅ Live, wired to Reader App
- **Retention:** 90-day cron at 03:00 UTC

### LRS (Learning Record Store)
- **Domain:** (internal) | **Server:** :3111 | **Client dev:** :5186
- **Standard:** xAPI 1.0.3
- **Disk:** `/home/ubuntu/lrs/`
- **Status:** ✅ Live. Permanent record for compliance.

### Learner Bot
- **Domain:** (internal) | **Server:** :3120
- **Disk:** `/home/ubuntu/learner-bot/`
- **DB:** `learner_bot` on shared MySQL
- **Status:** ✅ Code complete. Nightly run verified 2026-04-17 (8 test learners, 0 errors).
- **Cron:** 04:00 UTC nightly. 03:00 UTC telemetry retention cleanup.
- **Auth:** X-Internal-Key on all routes.

### Content Service
- **Domain:** (internal) | **Server:** :3112 | **Client dev:** :5187
- **Disk:** `/home/ubuntu/content-meta/`
- **DB:** `content_meta` on shared MySQL
- **Status:** ✅ Live

### Analytics + ETL + Model Updater
- **Domain:** (internal) | **Server:** :3114 | **Client dev:** :5189
- **Status:** ✅ Live
- **Cron:** 02:30 UTC model updater; 03:30 UTC GDPR retention

### Learner Profile Service
- **Domain:** learner.readingtester.com | **Server:** :3109 | **Client dev:** :5184
- **DB:** `learner` on shared MySQL
- **Status:** ✅ Live — 2 test profiles only.

### Interest Onboarding / COPPA Gate
- **Domain:** onboard.readingtester.com | **Server:** :3113 | **Client dev:** :5188
- **DB:** `onboard` on shared MySQL
- **Status:** ✅ Built. **Legal blocker:** SendGrid key required from Sig. Email delivery not wired (logs to console).

## Services Currently Returning 502 (Must Fix — Phase 3)

| Service | Domain | Code Location |
|---|---|---|
| Voice Assessment | voice.readingtester.com | `/home/ubuntu/Voice/` |
| Decodables | decodable.readingtester.com | disk (Flask + React) |
| Talk AI | talk.readingtester.com | — |

## Shared Infrastructure

- **MySQL 8 Docker container:** `shared-db`, host port `3316`
- **Root password:** stored in secure env on server — contact server admin
- **Connect from containers:** `mysql://user:pw@172.17.0.1:3316/dbname`
- **Caddy:** HTTPS reverse proxy for all services
- **Rule:** `docker compose down && up` (never restart) after `.env` changes
